/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.uml_generator.transformer;

import java.util.ArrayList;
import java.util.List;

import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

import com.genia.toolbox.uml_generator.bean.ModelGeneratorContext;
import com.genia.toolbox.uml_generator.model.AbstractClassModel;
import com.genia.toolbox.uml_generator.model.AssociationCardinality;
import com.genia.toolbox.uml_generator.model.AssociationModel;
import com.genia.toolbox.uml_generator.model.AssociationType;
import com.genia.toolbox.uml_generator.model.impl.AssociationEnd;
import com.genia.toolbox.uml_generator.model.impl.AssociationModelImpl;

/**
 * {@link InputHandler} to handle associations.
 */
public class AssociationInputHandler
    extends AbstractInputHandler<AbstractClassModel>
{

  /**
   * register a new {@link AssociationModel} if it is navigable.
   * @param associationName
   *          the name of the association for debugging incorrect models
   * @param context
   *          the generation context
   * @param associationModel
   *          the {@link AssociationModel} to register
   */
  private void registerAssociation(final String associationName, final ModelGeneratorContext context, final AssociationModel associationModel)
  {
    if (!associationModel.getOtherEnd().isNavigable()) {
      return;
    }
    if (associationModel.getCurrentType() == null) {
      context.getErrors().add("(" + associationName + ") Association " + associationModel.getName() + " has no Type...");
    }
    associationModel.getCurrentType().addAssociation(associationModel);
    if (!StringUtils.hasText(associationModel.getComment())) {
      context.getErrors().add("(" + associationName + ") There must be a comment for association " + associationModel.getName() + " of object " + associationModel.getAssociatedType().getQualifiedName());
    }
    if (associationModel.getOtherEndAssociation() == null && associationModel.getCurrentCardinality() == AssociationCardinality.ONE) {
      context.getErrors().add("(" + associationName + ") association " + associationModel.getName() + " of object " + associationModel.getCurrentType().getQualifiedName()
          + " is one-to-? uni. This should not exists, please change to many-to-? uni, or one-to-? bidi");
    }
  }



  /**
   * the actual method that parse the current {@link Element}.
   * 
   * @param context
   *          the generation context
   * @param firstPass
   *          wether it is the first or second pass
   * @param element
   *          the element to parse
   * @param parent
   *          the generated parent model
   */
  public void processContent(final ModelGeneratorContext context, final boolean firstPass, final Element element, final AbstractClassModel parent)
  {
    if (!firstPass) {
      final String associationName = element.getAttribute("name");
      List<AssociationEnd> associationEnds = new ArrayList<AssociationEnd>();
      for (Element subElement : getElementsByPath(context, element, "UML:Association.connection", "UML:AssociationEnd")) {
        AssociationEnd associationEnd = new AssociationEnd();
        fillCommonValue(context, subElement, associationEnd);
        for (Element tmpTypeElement : getXmlManager().getElements("UML:AssociationEnd.participant", subElement.getChildNodes())) {
          for (Element typeElement : getXmlManager().getElements(tmpTypeElement.getChildNodes())) {
            associationEnd.setType((AbstractClassModel) context.getObject(typeElement.getAttribute("xmi.idref")));
            if (associationEnd.getType() == null) {
              context.getErrors().add("Unable to find type for association " + associationName);
            }
          }
        }
        if ("composite".equals(subElement.getAttribute("aggregation"))) {
          associationEnd.setAssociationType(AssociationType.COMPOSITE);
        }
        associationEnd.setNavigable(Boolean.parseBoolean(subElement.getAttribute("isNavigable")));
        for (Element multiplicityElement : getElementsByPath(context, subElement, "UML:AssociationEnd.multiplicity", "UML:Multiplicity", "UML:Multiplicity.range", "UML:MultiplicityRange")) {
          associationEnd.setCardinality("1".equals(multiplicityElement.getAttribute("upper")) ? AssociationCardinality.ONE : AssociationCardinality.MANY);
        }
        for (Element attributeElement : getElementsByPath(context, subElement, "UML:AssociationEnd.qualifier", "UML:Attribute")) {
          associationEnd.setQualifier(parseAttributeModel(context, attributeElement));
        }
        if (associationEnd.getQualifier() != null) {
          associationEnd.setCardinality(AssociationCardinality.MANY);
        }
        associationEnds.add(associationEnd);
      }
      AssociationEnd end1 = associationEnds.get(0);
      AssociationEnd end2 = associationEnds.get(1);
      AssociationModel associationModel1 = new AssociationModelImpl();
      AssociationModel associationModel2 = new AssociationModelImpl();

      associationModel1.setCurrentEnd(end1);
      associationModel1.setOtherEnd(end2);
      associationModel2.setCurrentEnd(end2);
      associationModel2.setOtherEnd(end1);
      if (end1.isNavigable() && end2.isNavigable()) {
        associationModel1.setOtherEndAssociation(associationModel2);
        associationModel2.setOtherEndAssociation(associationModel1);
      }
      registerAssociation(associationName, context, associationModel1);
      registerAssociation(associationName, context, associationModel2);

    }
  }

}
